package com.terrynoya.fight.evaluation
{
	import com.terrynoya.fight.emuns.Operator;
	import com.terrynoya.fight.emuns.SymbolType;
	import com.terrynoya.fight.evaluation.token.BinaryOperatorData;
	import com.terrynoya.fight.evaluation.token.CustomFunctionData;
	import com.terrynoya.fight.evaluation.token.NumberData;
	import com.terrynoya.fight.evaluation.token.RangeData;
	import com.terrynoya.fight.evaluation.token.UnaryOperatorData;
	import com.terrynoya.fight.evaluation.token.matcher.AlwaysTrueMatcher;
	import com.terrynoya.fight.util.Integer;

	public class TreeBuilder
	{
		private var _system:EvaluationSystem;
		
		public function TreeBuilder(system:EvaluationSystem)
		{
			this._system = system;
		}
		
		public function buildTree(tokens:Array):Array
		{
			var tokenIndex:Integer = new Integer();
			var output:Array = new Array();
			while(this.getToken(tokens,tokenIndex) != null)
			{
				var node:Node = this.parseNode(tokens,tokenIndex);
				if(node == null)
				{
					return output;	
				}
				output.push(node);
				if(this.getToken(tokens,tokenIndex) != null)
				{
					if(this.getToken(tokens,tokenIndex).asSymbol == SymbolType.Comma)
					{
						tokenIndex.value ++;
					}
					else
					{
						return output;
					}
				}
			}
			return output;
		}
		
		public function parseNode(tokens:Array,tokenIndex:Integer):Node
		{
			var lhs:Node = this.parseEndNode(tokens,tokenIndex);
//			trace(lhs.value);
			if(lhs == null)
			{
				return null;
			}
			for(var token:Token = this.getToken(tokens, tokenIndex); token != null; token = this.getToken(tokens, tokenIndex))
			{
				if(token.data is BinaryOperatorData == false)
				{
					break;
				}
				
				var operatorNode:Node = new Node(token);
				tokenIndex.value ++;
				
				var operator:int = BinaryOperatorData(token.data).operator;
				if(operator == Operator.Equals || operator == Operator.NotEquals)
				{
					var range:Node = this.parseRangeNode(tokens,tokenIndex);
					if(range != null)
					{
						range.chilren[0] = lhs;
						range.arguments[0] = operator;
						lhs = range;
						continue;
					}
				}
				
				var rhs:Node = this.parseNode(tokens,tokenIndex);
				if(rhs == null)
				{
					return null;
				}
				
				if(this.switchOrder(operatorNode,rhs))
				{
					lhs = this.breakTree(lhs,operatorNode,rhs);
				}
				else
				{
					operatorNode.chilren.push(lhs);
					operatorNode.chilren.push(rhs);
					lhs = operatorNode;
				}
			}
			return lhs;
		}
		
		private function switchOrder(lhs:Node,rhs:Node):Boolean
		{
//			trace("switchOrder",lhs.value,rhs.value);
			var lhsData:BinaryOperatorData = lhs.token.data as BinaryOperatorData;
			var rhsData:BinaryOperatorData = rhs.token.data as BinaryOperatorData;
			if(lhsData == null || rhsData == null)
			{
				return false;
			}
			return lhsData.precedence >= rhsData.precedence;
		}
		
		private function breakTree(lhs:Node,operatorNode:Node,rhs:Node):Node
		{
			operatorNode.chilren.push(lhs);
			var newBase:Node = rhs;
			while(newBase.chilren.length != 0 && Node(newBase.chilren[0]).chilren.length != 0 
				&& Node(newBase.chilren[0]).precedenceOverride == false && this.switchOrder(operatorNode,Node(newBase.chilren[0])) == true)
			{
				newBase = newBase.chilren[0];
			}
			operatorNode.chilren.push(newBase.chilren[0]);
			newBase.chilren[0] = operatorNode;
			return rhs;
		}
		
		/**
		 * 等于或者不等于 
		 * @return 
		 * 
		 */		
		private function parseRangeNode(tokens:Array,tokenindex:Integer):Node
		{
			var savedIndex:int = tokenindex.value;
			var tokenPre:Token = this.getToken(tokens,tokenindex);
			var gotoEnd:Boolean = false;
			if(tokenPre == null || (tokenPre.asSymbol != SymbolType.LeftBracket && tokenPre.asSymbol != SymbolType.LeftParen))
			{
				tokenindex.value = savedIndex;
				return null;
			}
			tokenindex.value ++;
			
			var nodeLeft:Node = this.parseNode(tokens,tokenindex);
			if(nodeLeft == null)
			{
				tokenindex.value = savedIndex;
				return null;
			}
			
			var tokenComma:Token = this.getToken(tokens,tokenindex);
			if (tokenComma == null || tokenComma.asSymbol != SymbolType.Comma)
			{
				tokenindex.value = savedIndex;
				return null;
			}
			tokenindex.value ++;
			
			var nodeRight:Node = this.parseEndNode(tokens,tokenindex);
			if(nodeRight == null)
			{
				tokenindex.value = savedIndex;
				return null;
			}
			
			var tokenPost:Token = this.getToken(tokens,tokenindex);
			if(tokenPost == null || 
				(tokenPost.asSymbol != SymbolType.RightBracket && tokenPost.asSymbol != SymbolType.RightParen)
			)
			{
				tokenindex.value = savedIndex;
				return null;
			}
			tokenindex.value++;
			
			var rangeNode:Node = new Node(new Token("",new RangeData(new AlwaysTrueMatcher())));
			rangeNode.chilren.push(null);
			rangeNode.chilren.push(nodeLeft);
			rangeNode.chilren.push(nodeRight);
			rangeNode.arguments.push(null);
			rangeNode.arguments.push(tokenPre.asSymbol);
			rangeNode.arguments.push(tokenPost.asSymbol);
			return rangeNode;
		}
		
		public function parseEndNode(tokens:Array,tokenindex:Integer):Node
		{
			var token:Token = this.getToken(tokens,tokenindex);
			var node:Node;
			if(token == null)
			{
				return null;
			}
			//数字
			if(token.data is NumberData)
			{
				tokenindex.value ++;
				node = new Node(token);
//				trace(node.value);
				return node;
			}
			
			if(token.data is UnaryOperatorData)
			{
				tokenindex.value ++;
				node = new Node(token);
				var child:Node = this.parseEndNode(tokens,tokenindex);
				node.addChild(child);
				return node;
			}
			
			if(token.asOperator == Operator.Plus || token.asOperator == Operator.Minus)
			{
				tokenindex.value ++;
				node = new Node(token);
				child = this.parseEndNode(tokens,tokenindex);
				if(child == null)
				{
					return null;
				}
				node.chilren.push(child);
				return node;
			}
			
			if(token.asSymbol == SymbolType.LeftParen)
			{
				var savedIndex:int = tokenindex.value;
				tokenindex.value ++;
				node = this.parseNode(tokens,tokenindex);
				if(node != null)
				{
					var endToken:Token = this.getToken(tokens,tokenindex);
					if(endToken != null && endToken.asSymbol == SymbolType.RightParen)
					{
						tokenindex.value ++;
						node.precedenceOverride = true;
						return node;
					}
				}
				tokenindex.value = savedIndex;
			}
			//自定义函数
			if(token.data is CustomFunctionData)
			{
				var data:CustomFunctionData = CustomFunctionData(token.data);
				node = new Node(token); 
				tokenindex.value++;
				
				var state:ParseState = new ParseState(this._system,this,node,tokens,tokenindex.value);
				
				var parseNode:Node = data.parse(state);
				if(parseNode != null)
				{
					tokenindex.value = state.tokenIndex;
					return parseNode;
				}
				else
				{
					return null;
				}
			}
			return null;
		}
		
		private function getToken(tokens:Array,tokenIndex:Integer):Token
		{
			var index:int = tokenIndex.value;
			return index >= tokens.length|| index < 0 ? null : tokens[index];
		}
	}
}